﻿using System.Collections.Generic;
using System.Net.Sockets;
using System;
using Roborally.CommonData.GlobaleTypes;
using System.Text.RegularExpressions;
using Roborally.CommonData.Enums;

namespace Roborally.Server
{
    public class Manager
    {
        private List<Speler> lstSpelers;
        private List<Kamer> lstKamers;
        private Protocollaag.Protocollaag protocollaag;
        private static Manager instance = null;
        public static Manager Instance
        {
            get { return instance; }
        }

        /// <summary>
        /// private int dat iedere speler zijn unique id geeft.
        /// </summary>
        private int aantalspeleruniqueidnext = 0;
        private int aantalkameruniqueidnext = 0;

        public Manager()
        {
            instance = this;
            lstSpelers = new List<Speler>();
            lstKamers = new List<Kamer>();

            protocollaag = new Protocollaag.Protocollaag();
            protocollaag.nieuwespeler += protocollaag_nieuwespeler;
            protocollaag.ontvangbericht += protocollaag_ontvangbericht;
            protocollaag.disconnectclient += protocollaag_disconnect;
            protocollaag.joinkamer += protocollaag_joinkamer;
            protocollaag.StartServer();

            //subscribe op events die ontvangen en shizzles.
            //
            //GUI-doet iets, in controller: UpdateSpelSituatie(int id,string data)
        }

        private void protocollaag_spelstarten(int SpelerID)
        {
            for (int i = 0; i < lstKamers.Count; i++)
            {
                if (lstKamers[i].SpelerIDs[0] == SpelerID)
                {
                    Console.WriteLine("Kamer" + i + " wordt gestart door speler '" + SpelerByID(SpelerID).SpelerNaam + "'");
                    for (int j = 0; j < lstKamers[i].SpelerIDs.Count; j++)
                    {
                        VerstuurData(BerichtType.SpelBegint, lstKamers[i].SpelerIDs[j], lstKamers[i].BordString);//hier komt uiteindelijk misschien iets anders dan de bordstring want dan is die al verstuurd tijdens het joinen van de kamer
                    }
                    lstKamers[i].StartNieuwSpel();
                }
            }
        }

        // Deze method zorgt er voor dat een speler in een kamer joint
        private void protocollaag_joinkamer(object sender, JoinKamerClientX client)
        {
            for (int i = 0; i < lstSpelers.Count; i++)
            {
                if(lstSpelers[i].SpelerTCPClient == client.TcpClient)
                {
                    int spelerid = lstSpelers[i].SpelerID;
                    JoinKamer(spelerid, client.KamerNaam);
                }
            }
        }

        void protocollaag_nieuwespeler(object sender, NieuweSpelerArgs Nieuwespeler)
        {
            Console.WriteLine(Nieuwespeler.Naam() + " is binnen!");
            lstSpelers.Add(new Speler(aantalspeleruniqueidnext++, Nieuwespeler.Naam(), Nieuwespeler.TcpClient()));
            if (checknaam(Nieuwespeler.Naam()))
            {
                lstSpelers.Add(new Speler(aantalspeleruniqueidnext++, Nieuwespeler.Naam(), Nieuwespeler.TcpClient()));
                protocollaag.StuurBericht(BerichtType.NaamGoedkeuring, "accepted", Nieuwespeler.TcpClient());
            }
            else
            {
                protocollaag.StuurBericht(BerichtType.NaamGoedkeuring, "NOT ACCEPTED", Nieuwespeler.TcpClient());
            }
        }

        private Boolean checknaam(string name)
        {
            Console.WriteLine(name + " wordt gecontroleerd.");
            Regex alpha = new Regex("[^a-zA-Z]");
            if (alpha.IsMatch(name) || name.Length < 4)
            {
                Console.WriteLine(name + " is afgekeurd.");
                return false;
            }
            for (int i = 0; i < lstSpelers.Count; i++)
            {
                if (lstSpelers[i].SpelerNaam == name)
                {
                    Console.WriteLine(name + " is bezet.");
                    return false;
                }
            }
            Console.WriteLine(name + " is goedgekeurd.");
            return true;
        }

        private void protocollaag_disconnect(object sender, DisconnectClientX client)
        {
            if (client.AllClients())
            {
                for (int i = 0; i < lstSpelers.Count; i++)
                {
                    Disconnect(lstSpelers[i].SpelerTCPClient);
                }
            }
            else
            {
                Disconnect(client.TcpClient());
            }
        }

        private void protocollaag_ontvangbericht(object sender, VertaaldBinnenkomendBericht binnenkomendbericht)
        {
            string naam = "Unknown";
            int spelerID = 0;

            for (int i = 0; i < lstSpelers.Count; i++)
            {
                if (lstSpelers[i].SpelerTCPClient == binnenkomendbericht.TcpClient())
                {
                    naam = lstSpelers[i].SpelerNaam;
                    spelerID = lstSpelers[i].SpelerID;
                }
            }

            if (binnenkomendbericht.BerichtType() == BerichtType.ChatBericht)
            {
                Console.WriteLine("Chatbericht van " + naam + ": " + binnenkomendbericht.Bericht());
                ChatBericht(naam, binnenkomendbericht.Bericht());
            }
            else if (binnenkomendbericht.BerichtType() == BerichtType.Disconnect)
            {
                Disconnect(binnenkomendbericht.TcpClient());
            }
            else if (binnenkomendbericht.BerichtType() == BerichtType.JoinKamer)
            {
                Console.WriteLine(naam + " is de kamer " + binnenkomendbericht.Bericht() + " gejoined.");
                JoinKamer(spelerID, binnenkomendbericht.Bericht());
            }
            else if (binnenkomendbericht.BerichtType() == BerichtType.LeaveKamer)
            {
                Console.WriteLine(naam + " is de kamer " + binnenkomendbericht.Bericht() + " geleaved.");
                LeaveKamer(spelerID, binnenkomendbericht.Bericht());
            }
            else if (binnenkomendbericht.BerichtType() == BerichtType.MaakKamer)
            {
                Console.WriteLine(naam + " heeft een nieuwe kamer gemaakt met de naam Kamer" + lstKamers.Count);
                MaakKamer(naam, spelerID, binnenkomendbericht.Bericht());
            }
            else if (binnenkomendbericht.BerichtType() == BerichtType.NieuweNaam)
            {
                if (checknaam(binnenkomendbericht.Bericht()))
                {
                    if (verandernaam(binnenkomendbericht.Bericht(), binnenkomendbericht.TcpClient()))
                    {
                        Console.WriteLine(naam + " heeft een nieuwe naam gekozen: " + binnenkomendbericht.Bericht());
                        protocollaag.StuurBericht(BerichtType.NaamGoedkeuring, "accepted", binnenkomendbericht.TcpClient());
                    }
                }
                else
                {
                    protocollaag.StuurBericht(BerichtType.NaamGoedkeuring, "NOT ACCEPTED", binnenkomendbericht.TcpClient());
                }
            }
            else if (binnenkomendbericht.BerichtType() == BerichtType.SpelStarten)
            {
                protocollaag_spelstarten(spelerID);
            }
            else if (binnenkomendbericht.BerichtType() == BerichtType.SpelerReady)
            {
                PlayerReady(binnenkomendbericht.TcpClient());
            }
            else if (binnenkomendbericht.BerichtType() == BerichtType.VraagKamerLijst)
            {
                StuurKamers(binnenkomendbericht.TcpClient());
            }
            else if (binnenkomendbericht.BerichtType() == BerichtType.ClientData)
            {
                ClientData(spelerID, binnenkomendbericht.Bericht());
            }
            else
            {
                Console.WriteLine("Onbekend bericht ontvangen!");
            }
        }

        private void ClientData(int spelerID, string bericht)
        {
            for (int i = 0;i < lstKamers.Count; i++)
            {
                for (int j = 0; j < lstKamers[i].SpelerIDs.Count; j++)
                {
                    if (lstKamers[i].SpelerIDs[j] == spelerID)
                    {
                        Console.WriteLine("SpelerLegtKaartNeer:" + bericht);
                        if (lstKamers[i].Controller.UpdateSpelSituatie(spelerID, "SpelerLegtKaartNeer:"+bericht))
                        {
                            Console.WriteLine("Kaart goedgekeurt!");
                        }
                        else
                        {
                            Console.WriteLine("Kaart afgekeurt!");
                        }
                    }
                }
            }
        }

        private void LeaveKamer(int spelerID, string bericht = "")
        {
            for (int i = 0; i < lstKamers.Count; i++)
            {
                if (bericht != "")
                {
                    if (lstKamers[i].KamerNaam == bericht)
                    {
                        lstKamers[i].LeaveKamer(spelerID);
                    }
                }
                else
                {
                    lstKamers[i].LeaveKamer(spelerID);
                }
            }
        }

        private void JoinKamer(int spelerID, string bericht)
        {
            for (int i = 0; i < lstKamers.Count; i++)
            {
                if (lstKamers[i].KamerNaam == bericht)
                {
                    if (lstKamers[i].JoinKamer(spelerID))
                    {
                        string kamerJoinData = lstKamers[i].BordString; // deze string moet de bordstring bevatten
                        VerstuurData(BerichtType.JoinKamer, spelerID, kamerJoinData);

                        foreach (int spelerid in lstKamers[i].SpelerIDs)
                        {
                            VerstuurData(BerichtType.AndereSpelerJointKamer, spelerid, SpelerByID(spelerID).SpelerNaam);
                        }
                    }
                }
            }
        }

        private void StuurKamers(TcpClient client)
        {
            List<TcpClient> clients = new List<TcpClient>();
            clients.Add(client);
            VerstuurData(BerichtType.VraagKamerLijst, clients, "CLEAR LIST");
            for (int i = 0; i < lstKamers.Count; i++)
            {
                string bericht = lstKamers[i].KamerNaam + ";" + lstKamers[i].SpelerIDs.Count + ";" + lstKamers[i].SpelGestart;
                VerstuurData(BerichtType.VraagKamerLijst, clients, bericht);
            }
            VerstuurData(BerichtType.VraagKamerLijst, clients, "END OF LIST");
        }

        private void PlayerReady(TcpClient tcpClient)
        {
            for (int i = 0; i < lstSpelers.Count; i++)
            {
                if (lstSpelers[i].SpelerTCPClient == tcpClient)
                {
                    lstSpelers[i].ReadyState = true;
                }
            }
        }

        private void Disconnect(TcpClient tcpClient)
        {
            for (int i = 0; i < lstSpelers.Count; i++)
            {
                if (lstSpelers[i].SpelerTCPClient == tcpClient)
                {
                    Console.WriteLine(lstSpelers[i].SpelerNaam + " is gedisconnect.");
                    for (int j = 0; j < lstKamers.Count; j++)
                    {
                        lstKamers[j].LeaveKamer(lstSpelers[i].SpelerID);
                        if (lstKamers[j].SpelerIDs.Count == 0)
                        {
                            Console.WriteLine(lstKamers[j].KamerNaam + " is leeg en wordt dus verwijdert.");
                            lstKamers.Remove(lstKamers[j]);
                        }
                    }
                    LeaveKamer(lstSpelers[i].SpelerID);
                    protocollaag.netwerkconnectie.Disconnect(tcpClient, lstSpelers[i].SpelerNaam);
                    lstSpelers.Remove(lstSpelers[i]);
                }
            }
        }

        private void MaakKamer(string naam, int spelerID, string bericht)
        {
            lstKamers.Add(new Kamer(naam, spelerID, "Kamer" + aantalkameruniqueidnext++, bericht));
            VerstuurData(BerichtType.AndereSpelerJointKamer, spelerID, SpelerByID(spelerID).SpelerNaam);
        }

        /// <summary>
        /// Dit doet de chat versturen naar iedereen op de server
        /// </summary>
        /// <param name="naam"></param>
        /// <param name="bericht"></param>
        private void ChatBericht(string naam, string bericht)
        {
            List<TcpClient> clients = new List<TcpClient>();
            for (int i = 0; i < lstSpelers.Count; i++)
            {
                clients.Add(lstSpelers[i].SpelerTCPClient);
            }
            VerstuurData(BerichtType.ChatBericht, clients, ">>" + naam + " zegt: " + bericht);
        }

        private bool verandernaam(string naam, TcpClient tcpClient)
        {
            for (int i = 0; i < lstSpelers.Count; i++)
            {
                if (lstSpelers[i].SpelerTCPClient == tcpClient)
                {
                    lstSpelers[i].SpelerNaam = naam;
                    return true;
                }
            }
            return false;
        }

        public void VerstuurData(BerichtType type, int client_id, string bericht)
        {
            List<TcpClient> clients = new List<TcpClient>();
            for (int i = 0; i < lstSpelers.Count; i++)
            {
                if (lstSpelers[i].SpelerID == client_id)
                {
                    clients.Add(lstSpelers[i].SpelerTCPClient);
                }
            }
            VerstuurData(type, clients, bericht);
        }

        public void VerstuurData(BerichtType type, List<TcpClient> clients, string bericht)
        {
            protocollaag.StuurBericht(type, bericht, clients);
        }

        public Speler SpelerByID(int spelerID)
        {
            for (int i = 0; i < lstSpelers.Count; i++)
            {
                if (lstSpelers[i].SpelerID == spelerID)
                {
                    return lstSpelers[i];
                }
            }
            throw new Exception("Geen speler gevonden");
        }
    }
}